文章目录
  1. 1. submodule
    1. 1.1. 概念
    2. 1.2. 使用方式
    3. 1.3. 缺点
  2. 2. subtree
    1. 2.1. 概念
    2. 2.2. 使用方式
    3. 2.3. 缺点
  3. 3. submodule与subtree各自适用的场景
  4. 4. references

场景是,我的git仓库需要依赖一个单独的子git仓库,而且我希望每次它的更新我都可以通过操作本仓库直接获取到,但又不属于我管理。在这样的需求下我注意到了git提供的submodule命令。

submodule

概念

submodule是git最初为子模块这样一个需求而建立的功能,其作用正如其名,可以让你在你的仓库中导入别人的项目,保持它独立的提交历史,但是与你自己的仓库同步。它很方便你解决库依赖问题,如果你依赖于某个库的某次提交,使用这种方式可以保证你的仓库始终依赖于同一个子模块。如果该子模块缺失(而不是主动移除),你的修改将不可提交。

使用方式

概念中提到了,父项目中保存的是子模块的提交,而不是别的什么,所以当你在子模块中修改了什么并提交之后,父项目就会知道你当前HEAD改变了,于是会记录这个改变。

可能的使用场景是:

  • 你所依赖的仓库非常活跃,API变化频繁,极有可能导致你父项目不能正常工作。此时你可以锁定某个提交。
  • 你依赖于一个几乎不变的仓库。这时候你也可以锁定某个提交,作为引用依赖。
  • 当你想把项目中的一部分交由第三方开发(并作为你的子模块)的时候,你可以选择特定的提交来集成。

使用的命令:

git submodule [--quiet] add [-b <branch>] [-f|--force] [--name <name>]  # same with git add
              [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]  
git submodule [--quiet] status [--cached] [--recursive] [--] [<path>…]  # same with git status
git submodule [--quiet] init [--] [<path>…]  # same with git clone
git submodule [--quiet] deinit [-f|--force] [--] <path>…  
git submodule [--quiet] update [--init] [--remote] [-N|--no-fetch]  
              [-f|--force] [--rebase|--merge] [--reference <repository>]  
              [--depth <depth>] [--recursive] [--] [<path>…]  
git submodule [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]  
              [commit] [--] [<path>…]  
git submodule [--quiet] foreach [--recursive] <command>  # used when submodule has submodules
git submodule [--quiet] sync [--recursive] [--] [<path>…]  # used when submodule's remote changed

如果你经常使用submodule,可以在~/.gitconfig中为submodule命令设置别名,如下:

[alias]
st = status -s
ci = commit
l = log --oneline --decorate -12 --color
ll = log --oneline --decorate --color
lc = log --graph --color
co = checkout
br = branch
rb = rebase
dci = dcommit
sbi = submodule init
sbu = submodule update
sbp = submodule foreach git pull
sbc = submodule foreach git co master

缺点

虽然开始用的时候submodule看起来很方便很美,不幸的是,实际使用中缺点不少。

  1. 如果你要在父项目中切换到别的不含submodule的分支工作,你会得到一个警告,说你的submodule目录无法删除
  2. 如果你想在这个submodule中直接修改提交什么东西,想要共享给所有引用它的项目,那是不行的,在修改子模块的时候,需要注意submodule默认是在HEAD上的,并没有在任何分支上,所以如果你要在submodule中修改并且提交到某个分支上,需要先checkout这个分支,再做修改和提交(如果已经在HEAD上提交了,那么请记住提交的hash,checkout master,使用cherry-pick把更改拉到master上)
  3. 以上,如果你只想要在本地修改这个submodule,而不想提交到仓库(从而避免影响依赖它的其他项目),那是不行的,你将会无法提交父项目
  4. 含有submodule的项目,你每次pull的时候都最好要手动执行一次git submodule update来更新,如果submodule有了更新而你没有及时update,不幸你又git commit -a提交了父项目,那么你的子模块就没有更新
  5. git submodule update的坑在于,这是个强制覆盖的行为,如果你对子模块有任何改动,需要先提交,否则就会被覆盖
  6. 需要移除子模块的时候步骤比较多。submodule子模块信息在.gitmodules文件中,并且在.git/config里面存有副本。因此移除的时候不是简单地删文件夹了事,需要手动执行以下步骤:
    • .gitmodules中删除相关行
    • .git/config中删除相关行
    • 运行git rm -cached path_to_submodule
    • 删除子模块文件,提交


在搜索解决submodule问题的过程中得知了具有相似功能但据称更好用的subtree功能。

subtree

概念

直译叫做子树,从名字可以看出它在概念上的设计,如果一个项目是一棵树,它就是作为子树而存在的。事实上,它将你所指定的仓库内容合并到了父项目中。在git 1.7.11+版本中可以使用,但可以支持早期的git仓库。相较于submodule,它的优势在于不会添加额外的元数据,而且在父项目clone好之后子项目也就好了,不需要手动update或者init,你甚至感觉不到subtree的存在,同时它允许你对subtree内容做修改而不影响原始仓库。

使用方式

subtree的命令比submodule少一些,因此workflow上更加清楚
例如,将vim某个插件作为子树放到bundle下指定目录

git subtree add --prefix .vim/bundle/tpope-vim-surround \
https://bitbucket.org/vim-plugins-mirror/vim-surround.git master --squash

没有initupdate

缺点

缺点非常明显,至少到我写本文时为止,存在以下问题:

  1. 一次提交是针对父项目还是子树,这个维护需要你自己做
  2. 对于子树上游代码的更新较为复杂
  3. subtree有不同的merge策略,需要学习
  4. 初始化的时候命令有点长

上游代码更新和subtree merge可以参考alternatives-to-git-submodule-git-subtree

submodule与subtree各自适用的场景

从以上对比中可以看出,submodule和subtree具有各自适用的场景:

submodule 适用特点: 仓库单独管理; 只依赖于某个特定提交; 上游端较不活跃;

subtree 适用特点: 希望隐藏subtree,作为一整个仓库管理; 简化子模块维护流程; 对上游端贡献频率低

git 1.5.2以后建议使用subtree管理子模块

references

Git submodule VS Git Subtree
非常棒的 SubTree 教程
一个真实场景的使用例子
alternatives-to-git-submodule-git-subtree
Git Submodules: Core concept, workflows and tips
为什么使用Git Subtree - 百度文库
Git Submodule的坑
使用Git Submodule可能遇到的坑
Git深度使用经验总结
understanding git submodules

文章目录
  1. 1. submodule
    1. 1.1. 概念
    2. 1.2. 使用方式
    3. 1.3. 缺点
  2. 2. subtree
    1. 2.1. 概念
    2. 2.2. 使用方式
    3. 2.3. 缺点
  3. 3. submodule与subtree各自适用的场景
  4. 4. references